﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using FimetContoDeposito.BLL.Facades;
using FimetContoDeposito.BLL.Objects;
using FimetContoDeposito.DAL;
using GrayParrot.Core;

namespace FimetContoDeposito.BLL
{
    public class ElaborazioneMovimentiManager
    {
        public ElaborazioneMovimentiManager()
        {

        }

        public ContributoScarico CalcolaContributoScarico(Guid idArticolo, Guid idMovimento, decimal quantitaDaScaricare)
        {
            ContributoScarico retValue = null;

            decimal m_qtaPacco = 0;
            using (AnagraficaArticoliFacade m_aaf = AnagraficaArticoliFacade.Create())
            {
                ReturnValue<decimal> m_qtaPaccoRes = m_aaf.GetQtaPacco(idArticolo);
                if (m_qtaPaccoRes.HasValue)
                {
                    m_qtaPacco = m_qtaPaccoRes.Value;
                }
            }

            if ((m_qtaPacco != 0) && (quantitaDaScaricare > m_qtaPacco))
            {
                retValue = new ContributoScarico();
                retValue.K = (int)Math.Truncate(quantitaDaScaricare / m_qtaPacco);
                retValue.MovimentoPezzi = quantitaDaScaricare - (retValue.K * m_qtaPacco);

                using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                {
                    ReturnValue<decimal> m_fLottoRes = m_mdf.GetFLotto(idMovimento);

                    if (m_fLottoRes.HasValue)
                    {
                        retValue.MovimentoKg = retValue.MovimentoPezzi * m_fLottoRes.Value;

                        retValue.QtaFatt = quantitaDaScaricare * m_fLottoRes.Value;
                    }
                }
            }

            return retValue;
        }

        public ContributoScarico CalcolaContributoScarico(Guid idArticolo, MovimentoBag movimentoBag)
        {
            ContributoScarico retValue = null;

            decimal m_qtaPacco = 0;
            using (AnagraficaArticoliFacade m_aaf = AnagraficaArticoliFacade.Create())
            {
                ReturnValue<decimal> m_qtaPaccoRes = m_aaf.GetQtaPacco(idArticolo);
                if (m_qtaPaccoRes.HasValue)
                {
                    m_qtaPacco = m_qtaPaccoRes.Value;
                }
            }

            if ((m_qtaPacco != 0) && (movimentoBag.QuantitaDaScaricare > m_qtaPacco))
            {
                retValue = new ContributoScarico();
                retValue.K = (int)Math.Ceiling(movimentoBag.QuantitaDaScaricare / m_qtaPacco);
                retValue.MovimentoPezzi = movimentoBag.QuantitaDaScaricare - (retValue.K * m_qtaPacco);

                using(MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                {
                    ReturnValue<decimal> m_fLottoRes = m_mdf.GetFLotto(movimentoBag.Id);

                    if (m_fLottoRes.HasValue)
                    {
                        retValue.MovimentoKg = retValue.MovimentoPezzi * m_fLottoRes.Value;

                        retValue.QtaFatt = movimentoBag.QuantitaDaScaricare * m_fLottoRes.Value;
                    }
                }
            }

            return retValue;
        }

        /// <summary>
        /// Prepara gli scarichi da effettuare dai movimento di carico relativi ad un articolo
        /// sulla base della quantità indicata da scaricare...
        /// </summary>
        /// <param name="idArticolo">identificatore articolo.</param>
        /// <param name="quantitaRichiestaDaScaricare">quantita richiesta da scaricare.</param>
        /// <returns></returns>
        public List<MovimentoBag> RichiestaScarico(Guid idArticolo, decimal quantitaRichiestaDaScaricare)
        {
            List<MovimentoBag> retValue = new List<MovimentoBag>();

            Guid m_idArticolo = idArticolo;
            decimal m_quantitaRichiestaDaScaricare = quantitaRichiestaDaScaricare;

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                ReturnValue<IQueryable<MovimentoBag>> m_rt = m_mf.GetPropostaScarico(m_idArticolo);

                if ((m_rt.HasValue) && (m_rt.Value != null))
                {
                    List<MovimentoBag> m_movimentiDiCaricoProposti = m_rt.Value.ToList();

                    decimal m_qr = m_quantitaRichiestaDaScaricare;

                    foreach (MovimentoBag bag in m_movimentiDiCaricoProposti)
                    {
                        decimal m_quantitaDisponibile = bag.Quantita - bag.Prenotata;

                        if (m_qr == 0)
                        {
                            break;
                        }

                        if (m_quantitaDisponibile >= m_qr)
                        {
                            bag.QuantitaDaScaricare = m_qr;
                            m_qr = 0;

                            //m_qr = m_qr - m_quantitaRichiestaDaScaricare;
                        }
                        else
                        {
                            bag.QuantitaDaScaricare = m_quantitaDisponibile;
                            m_qr = m_qr - m_quantitaDisponibile;

                            // la quantita del movimento è < della quantita richiesta da scaricare
                            // quindi esaurisco il movimento di carico e lo chiudo
                            bag.Chiuso = true;
                        }

                        retValue.Add(bag);
                    }
                }
            }

            return retValue;
        }

        public ReturnValue<bool> CancellaElementoCarico(Guid idElemento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            // devo verificare che l'elemento di un movimento di carico non sia usato in uno scarico
            // altrimenti non posso cancellarlo...
            using (DettaglioScaricoBollaFacade m_dscf = DettaglioScaricoBollaFacade.Create())
            {
                retValue = m_dscf.CheckInScarico(idElemento);

                if (retValue.HasValue && retValue.Value)
                {
                    retValue.Message = "L'elemento è utilizzato in un movimento di scarico. Non è possibile cancellarlo !";
                }
                else
                {
                    if (retValue.HasValue && !retValue.Value)
                    {
                        // l'elemento non è coinvolto in scarichi, posso cancellare l'elemento...
                        using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                        {
                            retValue = m_mdf.SetElementoCancellato(idElemento);
                        }
                    }
                }
            }

            return retValue;
        }

        /// <summary>
        /// Cancella un elemento di un movimento di scarico...
        /// </summary>
        /// <param name="idArticolo">The identifier articolo.</param>
        /// <returns></returns>
        public ReturnValue<bool> CancellaElementoScarico(Guid idElemento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                retValue = m_mf.RipristinaDettagliMovimenti(idElemento);

                if (retValue.HasValue && retValue.Value)
                {
                    retValue = CancellaElemento(idElemento);
                }
            }

            return retValue;
        }

        /// <summary>
        /// Cancella the elementi scarico...
        /// </summary>
        /// <param name="idMovimento">The identifier movimento.</param>
        /// <returns></returns>
        public ReturnValue<bool> CancellaElementiScarico(Guid idMovimento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                ReturnValue<IEnumerable<Guid>> m_dettagliRV = m_mf.GetMovimentoDettaglio(idMovimento);

                if (m_dettagliRV.HasValue && m_dettagliRV.Value != null)
                {
                    foreach (Guid id in m_dettagliRV.Value)
                    {
                        CancellaElementoScarico(id);
                    }

                    retValue.HasValue = true;
                    retValue.Value = true;
                }
            }

            return retValue;
        }

        /// <summary>
        /// Cancella gli elementi di un movimento di carico...
        /// </summary>
        /// <param name="idMovimento">The identifier movimento.</param>
        /// <returns></returns>
        public ReturnValue<bool> CancellaElementiCarico(Guid idMovimento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                ReturnValue<IEnumerable<Guid>> m_dettagliRV = m_mf.GetMovimentoDettaglioCarico(idMovimento);

                if (m_dettagliRV.HasValue && m_dettagliRV.Value != null)
                {
                    foreach (Guid id in m_dettagliRV.Value)
                    {
                        CancellaElementoCarico(id);
                    }

                    retValue.HasValue = true;
                    retValue.Value = true;
                }
            }

            return retValue;
        }

        public ReturnValue<bool> CancellaElementoScaricoModificato(Guid idElemento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                retValue = m_mf.RipristinaDettagliMovimenti(idElemento);

                if (retValue.HasValue && retValue.Value)
                {
                    retValue = CancellaElementoModificato(idElemento);
                }
            }

            return retValue;
        }

        public ReturnValue<bool> CancellaElemento(Guid idElemento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                ReturnValue<int> m_rvNumeroElementi = m_mf.GetNumeroElementiScarico(idElemento);
                if (m_rvNumeroElementi.HasValue && m_rvNumeroElementi.Value != -1 && m_rvNumeroElementi.Value > 0)
                {
                    ReturnValue<FCD_MovimentiDettaglio> m_rvDdettaglio = new ReturnValue<FCD_MovimentiDettaglio>(null);
                    FCD_MovimentiDettaglio m_dettaglio = null;

                    // cancello il dettaglio movimento relativo allo scarico...
                    using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                    {
                        m_rvDdettaglio = m_mdf.Get(idElemento);
                        if (m_rvDdettaglio.HasValue && m_rvDdettaglio.Value != null)
                        {
                            m_dettaglio = m_rvDdettaglio.Value;

                            if (m_rvNumeroElementi.Value > 1)
                            {
                                retValue = m_mdf.SetElementoCancellato(m_dettaglio.Id);
                            }
                            else
                            {
                                // cancello il dettaglio movimento relativo allo scarico,
                                // cancello anche il movimento di scarico...
                                retValue = CancellaMovimentoScarico(m_dettaglio.IdMovimento);
                            }
                        }
                    }
                }
            }

            return retValue;
        }

        public ReturnValue<bool> CancellaElementoModificato(Guid idElemento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                ReturnValue<int> m_rvNumeroElementi = m_mf.GetNumeroElementiScarico(idElemento);
                if (m_rvNumeroElementi.HasValue && m_rvNumeroElementi.Value != -1 && m_rvNumeroElementi.Value > 0)
                {
                    ReturnValue<FCD_MovimentiDettaglio> m_rvDdettaglio = new ReturnValue<FCD_MovimentiDettaglio>(null);
                    FCD_MovimentiDettaglio m_dettaglio = null;

                    // cancello il dettaglio movimento relativo allo scarico...
                    using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                    {
                        m_rvDdettaglio = m_mdf.Get(idElemento);
                        if (m_rvDdettaglio.HasValue && m_rvDdettaglio.Value != null)
                        {
                            m_dettaglio = m_rvDdettaglio.Value;

                            retValue = m_mdf.SetElementoCancellato(m_dettaglio.Id);
                        }
                    }
                }
            }

            return retValue;
        }

        /// <summary>
        /// Cancella un movimento...
        /// </summary>
        /// <param name="idMovimento">The identifier movimento.</param>
        /// <returns></returns>
        /// <remarks>
        /// Cancella il movimento ed i suoi dettagli...
        /// </remarks>
        public ReturnValue<bool> CancellaMovimentoCarico(Guid idMovimento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                {
                    retValue = m_mdf.CheckDettagliMovimento(idMovimento);

                    if (retValue.HasValue && retValue.Value)
                    {
                        retValue.Message = "Elementi del movimento di carico sono stati utilizzati in scarichi. Non è possibile cancellare il movimento di carico";
                    }
                    else
                    {
                        retValue = m_mdf.SetCancellato(idMovimento);

                        if (retValue.HasValue && retValue.Value)
                        {
                            using (MovimentiFacade m_mf = MovimentiFacade.Create())
                            {
                                retValue = m_mf.SetCancellato(idMovimento);
                            }
                        }
                        else
                        {
                            if (retValue.HasValue && !retValue.Value)
                            {
                                retValue.Message = "Impostazione ad annullato per i movimenti dettaglio non riuscita.";
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        public ReturnValue<bool> CancellaMovimentoScarico(Guid idMovimento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                {
                    ReturnValue<IQueryable<FCD_MovimentiDettaglio>> m_rvDettagli = m_mdf.GetDettagliMovimento(idMovimento);

                    if (m_rvDettagli.HasValue && m_rvDettagli.Value != null)
                    {
                        List<FCD_MovimentiDettaglio> m_dettagli = m_rvDettagli.Value.ToList();

                        retValue = m_mdf.CheckDettagliMovimento(idMovimento);

                        if (retValue.HasValue && retValue.Value)
                        {
                            retValue.Message = "Elementi del movimento di carico sono stati utilizzati in scarichi. Non è possibile cancellare il movimento di carico";
                        }
                        else
                        {
                            retValue = m_mdf.SetCancellato(idMovimento);

                            if (retValue.HasValue && retValue.Value)
                            {
                                using (MovimentiFacade m_mf = MovimentiFacade.Create())
                                {
                                    foreach (FCD_MovimentiDettaglio d in m_dettagli)
                                    {
                                        retValue = m_mf.RipristinaDettagliMovimenti(d.Id);
                                    }

                                    retValue = m_mf.SetCancellato(idMovimento);
                                }
                            }
                            else
                            {
                                if (retValue.HasValue && !retValue.Value)
                                {
                                    retValue.Message = "Impostazione ad annullato per i movimenti dettaglio non riuscita.";
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        [Obsolete("DA CANCELLARE")]
        public ReturnValue<bool> CancellaMovimento(Guid idMovimento)
        {
            ReturnValue<bool> retValue = new ReturnValue<bool>(false);

            try
            {
                using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                {
                    retValue = m_mdf.DeleteAllDetails(idMovimento);
                }

                if (retValue.HasValue && retValue.Value)
                {
                    using (MovimentiFacade m_mf = MovimentiFacade.Create())
                    {
                        ReturnValue<FCD_Movimenti> m_rvMovimento = m_mf.Get(idMovimento);
                        if (m_rvMovimento.HasValue && m_rvMovimento.Value != null)
                        {
                            retValue = m_mf.Delete(m_rvMovimento.Value);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                retValue.HasError = true;
                retValue.Error = ex;
            }

            return retValue;
        }

        /// <summary>
        /// Modificas the elemento....
        /// </summary>
        /// <param name="idMovimento">The identifier movimento.</param>
        /// <param name="tipoMovimento">The tipo movimento.</param>
        /// <returns></returns>
        public ReturnValue<bool> ModificaElemento(Guid idElemento, Guid idMovimento, Guid idArticolo, decimal quantita, TipoMovimento tipoMovimento)
        {
            ReturnValue<bool> m_retValue = new ReturnValue<bool>(false);

            switch (tipoMovimento)
            {
                case TipoMovimento.Carico:
                    m_retValue = ModificaElementoCarico(idElemento, idMovimento, idArticolo, quantita);
                    break;
                case TipoMovimento.Scarico:
                    m_retValue = ModificaElementoScarico(idElemento, idMovimento, idArticolo, quantita);
                    break;
                default:
                    break;
            }

            return m_retValue;
        }

        /// <summary>
        /// Modifico un elemento di un movimento di carico...
        /// </summary>
        /// <param name="idElemento">The identifier elemento.</param>
        /// <param name="idMovimento">The identifier movimento.</param>
        /// <param name="idArticolo">The identifier articolo.</param>
        /// <param name="quantita">The quantita.</param>
        /// <returns></returns>
        public ReturnValue<bool> ModificaElementoCarico(Guid idElemento, Guid idMovimento, Guid idArticolo, decimal quantita)
        {
            ReturnValue<bool> m_retValue = new ReturnValue<bool>(false);

            // con l'id prendo il vecchio dettaglio...
            using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
            {
                //  erifico se l'elemento di carico è coinvolto in uno scarico...
                m_retValue = m_mdf.CheckDettagliMovimento(idMovimento);

                if (m_retValue.HasValue && !m_retValue.Value)
                {
                    // non è coinvolto in uno scarico, posso procedere...
                    ReturnValue<FCD_MovimentiDettaglio> m_rvMD = m_mdf.Get(idElemento);

                    if (m_rvMD.HasValue && m_rvMD.Value != null)
                    {
                        FCD_MovimentiDettaglio m_md = m_rvMD.Value;

                        if (m_md.Articolo != idArticolo)
                        {
                            // annullo il vecchio dettaglio...
                            m_retValue = m_mdf.SetElementoCancellato(m_md.Id);

                            if (m_retValue.HasValue && m_retValue.Value)
                            {
                                // creo il dettaglio nuovo...
                                FCD_MovimentiDettaglio m_newMD = new FCD_MovimentiDettaglio();
                                m_newMD.Articolo = idArticolo;
                                m_newMD.IdMovimento = idMovimento;
                                m_newMD.Note = m_md.Note;
                                m_newMD.Prenotata = m_md.Prenotata;
                                m_newMD.Quantita = quantita;
                                m_newMD.QuantitaDisponibile = quantita;

                                m_retValue = m_mdf.Add(m_newMD);
                            }
                        }
                        else
                        {
                            // cambio solo la quantita...
                            if (m_md.Quantita != quantita)
                            {
                                m_md.Quantita = quantita;
                                m_retValue = m_mdf.Edit(m_md);
                            }
                        }
                    }
                }
            }

            return m_retValue;
        }

        public ReturnValue<bool> ModificaElementoScarico(Guid idElemento, Guid idMovimento, Guid idArticolo, decimal quantita)
        {
            ReturnValue<bool> m_retValue = new ReturnValue<bool>(false);

            try
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    // ripristina le quantità e annulla l'elemento...
                    m_retValue = CancellaElementoScarico(idElemento);

                    using (MagazzinoFacade m_cf = MagazzinoFacade.Create())
                    {
                        ReturnValue<decimal> m_rvCalcG = m_cf.GetGiacenza(idArticolo);

                        if (m_rvCalcG.HasValue && m_rvCalcG.Value != 0)
                        {
                            if (m_rvCalcG.Value < quantita)
                            {
                                throw new Exception("Giancenza non sufficiente. Operazione interrotta.");
                            }
                        }
                    }

                    using (MovimentoDettaglioFacade m_mdf = MovimentoDettaglioFacade.Create())
                    {
                        List<MovimentoBag> m_scarichi = RichiestaScarico(idArticolo, quantita);

                        if (m_retValue.HasValue && m_retValue.Value)
                        {
                            // creo il nuovo scarico...
                            using (DettaglioScaricoBollaFacade m_dsbf = DettaglioScaricoBollaFacade.Create())
                            {
                                foreach (MovimentoBag bag in m_scarichi)
                                {
                                    FCD_DettaglioScaricoBolla m_dsb = new FCD_DettaglioScaricoBolla();
                                    m_dsb.IdBollaCarico = bag.Id;
                                    m_dsb.Quantita = bag.QuantitaDaScaricare;
                                    m_dsb.IdScarico = idElemento;

                                    m_mdf.SetQuantitaDisponibile(bag.Id, bag.QuantitaDaScaricare);

                                    m_dsbf.Add(m_dsb);
                                }
                            }
                        }
                    }

                    scope.Complete();

                    m_retValue.HasValue = true;
                    m_retValue.Value = true;
                }
            }
            catch (Exception ex)
            {
                m_retValue.HasError = true;
                m_retValue.Error = ex;
            }

            return m_retValue;
        }

        public ReturnValue<bool> AccettaMovimentoCliente(Guid m_idMovimento, int m_tipoMovimento, string operatore)
        {
            ReturnValue<bool> m_retValue = new ReturnValue<bool>(false);

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                m_retValue = m_mf.AccettaMovimentoCliente(m_idMovimento, (TipoMovimento)m_tipoMovimento, operatore);
            }

            return m_retValue;
        }

        public ReturnValue<bool> CancellaDistintaCarico(Guid idMovimento)
        {
            ReturnValue<bool> m_retValue = new ReturnValue<bool>(false);

            using (MovimentiFacade m_mf = MovimentiFacade.Create())
            {
                m_retValue = m_mf.SetCancellato(idMovimento);
            }

            return m_retValue;
        }
    }
}
